package wangwenjun.phase2.concurrencypattern.readwritelockseparation.stampedlock;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

/**
 * @author ChangLiang
 * @date 2020/6/12
 */
@Slf4j
public class ShareData {

    /**
     * 定义共享数据（资源）
     */
    private final List<Character> container = new ArrayList<>();

    /**
     * 创建工厂
     */
    private final StampedLock stampedLock = new StampedLock();

    private final int length;

    public ShareData(int length) {
        this.length = length;
        for (int i = 0; i < length; i++) {
            container.add(i, 'c');
        }
    }

    /**
     * 当少量写线程写的时候 大量的读线程会被挂起
     * @return
     * @throws InterruptedException
     */
    public char[] read() throws InterruptedException {
        long stamp = stampedLock.readLock();
        try {
            char[] buffer = new char[length];
            for (int i = 0; i < length; i++) {
                buffer[i] = container.get(i);
            }
            // mock read time consumption
            TimeUnit.SECONDS.sleep(1);
            return buffer;
        } finally {
            stampedLock.unlock(stamp);
        }
    }

    public char[] readOptimistic() throws InterruptedException {
        long stamp = stampedLock.tryOptimisticRead();
        char[] buffer = new char[length];
        for (int i = 0; i < length; i++) {
            buffer[i] = container.get(i);
        }

        if (!stampedLock.validate(stamp)) {
            log.trace("{} validate stamp fail", this.getClass().getSimpleName());
            // 升级为悲观锁
            stamp = stampedLock.readLock();
            try {
                buffer = new char[length];
                for (int i = 0; i < length; i++) {
                    buffer[i] = container.get(i);
                }
                // mock read time consumption
//            TimeUnit.SECONDS.sleep(1);
            } finally {
                stampedLock.unlock(stamp);
            }
        }
        return buffer;
    }


    public void write(char c) throws InterruptedException {
        long stamp = stampedLock.writeLock();
        try {
            for (int i = 0; i < length; i++) {
                container.add(i, c);
            }
            // mock write time consumption
            TimeUnit.MILLISECONDS.sleep(200);
        } finally {
            stampedLock.unlock(stamp);
        }
    }


}
